/*
 * Strips.java
 */
package strips;

import java.util.ArrayDeque;
import java.util.Iterator;

/**
 *
 * @author mire
 */
public class Strips {

    private ArrayDeque<Predicate> currentState;     //trenutno stanje
    private ArrayDeque<Predicate> finalState;       //ciljno stanje
    private ArrayDeque<Predicate> goalState;        //ciljno stanje koje modifikujemo
    private ArrayDeque<Action> actionStack;         //stek za akcija
    private ArrayDeque<Action> actions;             //sve odradjene akcije
    private ArrayDeque<Block> globalAtributes;      //svi atributi
    private ArrayDeque<Predicate> temporaryGoals;   //stek za ciljeve

    public Strips(ArrayDeque<Predicate> initialState, ArrayDeque<Predicate> finalState) {
        this.currentState = initialState;
        this.finalState = finalState;
        this.goalState = finalState.clone();
        actionStack = new ArrayDeque<Action>();
        actions = new ArrayDeque<Action>();
        globalAtributes = new ArrayDeque<Block>();
        temporaryGoals = new ArrayDeque<Predicate>();
    }

    public ArrayDeque<Predicate> getNextState() {
        boolean isStateChanged = false;
        boolean loopDetected = false;
        int loop = 0;
        Predicate current = null;

        while (!isStateChanged) {                   //desila se promena   
            //ako smo stigli do kraja, da proverimo da se neshto promenilo
            if (goalState.isEmpty()) {
                if (currentState.containsAll(finalState)) {
                    return null;
                }
                goalState = finalState.clone();
                goalState.removeAll(currentState);
            }
            current = goalState.peek();

            //da li je akcija
            if (current.getName().equals("ACTION")) {
                goalState.pop();
                Action action = actionStack.pop();
                actions.addLast(action);
                if (currentState.containsAll(action.prequisition)) {
                    currentState.removeAll(action.delete);
                    //pushAll(currentState, action.add);
                    currentState.addAll(action.add);
                    isStateChanged = true;
                }
                break;
            }
            if (currentState.contains(current)) {
                goalState.pop();
                temporaryGoals.remove(current);
            } else {
                
                if(!temporaryGoals.contains(current))
                        temporaryGoals.add(current);
                        
                Action nextAction = current.getAction(loopDetected);
                nextAction.calculateАction();

                if (current.getArity() != nextAction.getArity()) //imamo nepoznat atribut
                {
                    Block missing = null;
                    if (globalAtributes.size() == 2) {
                        if (nextAction.getAtributes().contains(globalAtributes.peekFirst())) {
                            missing = globalAtributes.peekLast();
                        } else {
                            missing = globalAtributes.peekFirst();
                        }
                    } else {
                        missing = findUnknownAtribute(nextAction.getPrequisition());
                    }

                    nextAction.changeUnkownAtribute(missing);
                }

                //proveravamo da li dolazi do petlje
                if (loopDetected = loopDetection(temporaryGoals,nextAction.getPrequisition()))
                {
                    loop++;
                    if(loop > 1)    //u sluchaju da nam je dublja beskonachna petlja
                    {
                        temporaryGoals.removeLast();
                        Predicate goal = temporaryGoals.peekLast();
                        Predicate pred = null;
                        while(true)
                        {
                            pred = goalState.peek();
                            if(pred.equals(goal))
                                break;
                            if(pred.getName().equals("ACTION"))
                            {
                                actionStack.pop();
                            }
                            goalState.pop();
                        }
                    }
                    continue;
                }
                    
                //stavljamo akciju na stek
                actionStack.push(nextAction);
                goalState.push(new Predicate("ACTION"));
                if (!currentState.containsAll(nextAction.prequisition)) {
                    pushAll(goalState, nextAction.prequisition);
                }
            }

        }
        return currentState;

    }

    public ArrayDeque<Predicate> getGoalState() {
        return goalState;
    }
    
    

    public void setGlobalAtributes(ArrayDeque<Block> globalAtributes) {
        this.globalAtributes = globalAtributes;
    }

    public ArrayDeque<Action> getActions() {
        return actions;
    }

    public Action getLastAction() {
        return actions.peekLast();
    }

    //vraca predikat koji se prvi poklapa sa datim 
    public Block searchStateByName(String name) {
        Iterator<Predicate> it = currentState.iterator();
        while (it.hasNext()) {
            Predicate element = it.next();
            if (element.getName().equals(name)) {
                return element.getAtributes().peek();
            }
        }
        return null;
    }

    public Block searchStateByNameAndAttribute(String name, Block knownBlock, int pos) {
        Iterator<Predicate> it = currentState.iterator();
        while (it.hasNext()) {
            Predicate element = it.next();
            if (element.getName().equals(name)) {
                if (pos == 0 && element.getAtributes().peekFirst().equals(knownBlock)) {
                    return element.getAtributes().peekLast();
                } else if (pos == 1 && element.getAtributes().peekLast().equals(knownBlock)) {
                    return element.getAtributes().peekFirst();
                }
            }
        }
        return null;
    }

    public void pushAll(ArrayDeque<Predicate> state, ArrayDeque<Predicate> toBeAdded) {
        Iterator<Predicate> it = toBeAdded.descendingIterator();
        while (it.hasNext()) {
            state.push(it.next());
        }
    }
    
    private Block findUnknownAtribute(ArrayDeque<Predicate> actionPrereq) {
        Block missing = null;

        Iterator<Predicate> it = actionPrereq.iterator();
        Block found = null;
        while (it.hasNext()) {
            Block b = null;
            Predicate element = it.next();
            switch (element.getArity()) {
                case 2:
                    b = element.getAtributes().peekFirst();
                    int pos = 0;
                    if (b.getName().equals("Unknown")) {
                        b = element.getAtributes().peekLast();
                        pos = 1;
                    }
                    found = searchStateByNameAndAttribute(element.getName(), b, pos);
                    break;
                case 1:
                    b = element.getAtributes().peekFirst();
                    if (b.getName().equals("Unknown")) {
                        found = searchStateByName(element.getName());
                    }
                    break;
                case 0:
                    break;
            }
            if (found != null) {
                //missing = new Block(found);
                missing = found;
                break;
            }
        }
        return missing;
    }
    
    private boolean loopDetection(ArrayDeque<Predicate> temporaryGoals, ArrayDeque<Predicate> prequisition) {
        if( temporaryGoals == null || prequisition == null || temporaryGoals.isEmpty() || prequisition.isEmpty())
                return false;
        Iterator<Predicate> it = prequisition.iterator();
        while(it.hasNext())
        {
            Predicate element = it.next();
            if(temporaryGoals.contains(element))
                return true;
        }
        return false;
    }
}
